package com.ideaaedi.commonds.monitor;

import com.alibaba.fastjson2.JSON;
import com.ideaaedi.commonds.monitor.info.SystemCpuInfo;
import com.ideaaedi.commonds.monitor.info.SystemDiskInfo;
import com.ideaaedi.commonds.monitor.info.SystemMemoryInfo;
import com.ideaaedi.commonds.monitor.info.SystemOtherInfo;
import lombok.extern.slf4j.Slf4j;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 系统监控
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@Slf4j
public class SystemMonitorUtil {
    
    /**
     * 获取系统实时信息
     *
     * @param pretty
     *         是否格式化打印
     *
     * @return 系统实时信息
     */
    public static String systemRealTimeInfo(boolean pretty) {
        DecimalFormat decimalFormat = new DecimalFormat("#.##");
        SystemInfo systemInfo = new SystemInfo();
        
        StringBuilder sb = new StringBuilder(128);
        sb.append("\n ========================= system-monitor =========================\n");
        // 内存
        SystemMemoryInfo systemMemoryInfo = memoryInfo(systemInfo, decimalFormat);
        sb.append(" >>>>>>>>>>>>>>> memory start >>>>>>>>>>>>>>> " + "\n");
        sb.append(" Initial total memory(JVM):").append(systemMemoryInfo.getJvmInitialTotalMemory()).append("\n");
        sb.append(" Maximum available memory(JVM):").append(systemMemoryInfo.getJvmMaximumAvailableMemory()).append(
                "\n");
        sb.append(" Used memory(JVM):").append(systemMemoryInfo.getJvmUsedMemory()).append("\n");
        sb.append(" Total physical memory:").append(systemMemoryInfo.getPhysicalTotalMemory()).append("\n");
        sb.append(" Remaining physical memory:").append(systemMemoryInfo.getPhysicalRemainingMemory()).append("\n");
        sb.append(" Physical memory used:").append(systemMemoryInfo.getPhysicalUsedMemory()).append("\n");
        sb.append(" <<<<<<<<<<<<<<< memory  end  <<<<<<<<<<<<<<< ");
        sb.append("\n\n");
        
        // cpu
        SystemCpuInfo systemCpuInfo = cpuInfo(systemInfo, decimalFormat);
        sb.append(" >>>>>>>>>>>>>>> cpu start >>>>>>>>>>>>>>> ").append("\n");
        sb.append(" CPU core number:").append(systemCpuInfo.getCoreNumber()).append("\n");
        sb.append(" CPU system usage rate:").append(systemCpuInfo.getSystemUsageRate()).append("\n");
        sb.append(" CPU user usage rate:").append(systemCpuInfo.getUserUsageRate()).append("\n");
        sb.append(" CPU current io waiting rate:").append(systemCpuInfo.getCurrentIoWaitRate()).append("\n");
        sb.append(" CPU current idle rate:").append(systemCpuInfo.getCurrentIdleRate()).append("\n");
        sb.append(" <<<<<<<<<<<<<<< cpu  end  <<<<<<<<<<<<<<< ");
        sb.append("\n\n");
        
        // 磁盘
        List<SystemDiskInfo> systemDiskInfos = diskInfo(decimalFormat);
        sb.append(" >>>>>>>>>>>>>>> disk start >>>>>>>>>>>>>>> ").append("\n");
        for (SystemDiskInfo diskInfo : systemDiskInfos) {
            sb.append(" ").append(diskInfo.getPath()).append("\tTotalSpace:").append(diskInfo.getTotalSpace()).append(", "
                    + "UsableSpace:").append
                    (diskInfo.getUsableSpace()).append(", " + "FreeSpace:").append(diskInfo.getFreeSpace()).append("\n");
        }
        sb.append(" <<<<<<<<<<<<<<< disk  end  <<<<<<<<<<<<<<< ");
        sb.append("\n\n");
        
        // 其它信息
        SystemOtherInfo systemOtherInfo = otherInfo();
        sb.append(" >>>>>>>>>>>>>>> other start >>>>>>>>>>>>>>> ").append("\n");
        sb.append(" operating system:").append(systemOtherInfo.getOsName()).append("\n");
        sb.append(" Program start time:").append(systemOtherInfo.getProgramStartTime()).append("\n");
        sb.append(" Process pid:").append(systemOtherInfo.getProcessPid()).append("\n");
        sb.append(" JAVA_HOME:").append(systemOtherInfo.getJavaHome()).append("\n");
        sb.append(" JAVA_VERSION:").append(systemOtherInfo.getJavaVersion()).append("\n");
        sb.append(" USER_HOME:").append(systemOtherInfo.getUserHome()).append("\n");
        sb.append(" USER_NAME:").append(systemOtherInfo.getUserName()).append("\n");
        sb.append(" <<<<<<<<<<<<<<< other  end  <<<<<<<<<<<<<<< ");
        sb.append("\n");
        sb.append(" ========================= system-monitor ========================= ");
        String str = sb.toString();
        if (!pretty) {
            str = " " + str.replace("\n", "\t").trim();
        }
        return str;
    }
    
    /**
     * 获取系统实时信息
     *
     * @return 系统实时信息
     */
    public static com.ideaaedi.commonds.monitor.info.SystemInfo systemRealTimeInfo() {
        com.ideaaedi.commonds.monitor.info.SystemInfo systemInfo = new com.ideaaedi.commonds.monitor.info.SystemInfo();
        DecimalFormat decimalFormat = new DecimalFormat("#.##");
        SystemInfo oshiSystemInfo = new SystemInfo();
        systemInfo.setCpuInfo(cpuInfo(oshiSystemInfo, decimalFormat));
        systemInfo.setDiskInfos(diskInfo(decimalFormat));
        systemInfo.setMemoryInfo(memoryInfo(oshiSystemInfo, decimalFormat));
        systemInfo.setOtherInfo(otherInfo());
        return systemInfo;
    }
    
    /**
     * 获取内存信息
     *
     * @param systemInfo
     *         系统信息根对象(可为null)
     * @param decimalFormat
     *         数字格式化样式(可为null)
     *
     * @return 内存信息
     */
    public static SystemMemoryInfo memoryInfo(SystemInfo systemInfo, DecimalFormat decimalFormat) {
        if (systemInfo == null) {
            systemInfo = new SystemInfo();
        }
        if (decimalFormat == null) {
            decimalFormat = new DecimalFormat("#.##");
        }
        //noinspection AlibabaLowerCamelCaseVariableNaming
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        // 椎内存使用情况
        MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage();
        SystemMemoryInfo systemMemoryInfo = new SystemMemoryInfo();
        systemMemoryInfo.setJvmInitialTotalMemory(decimalFormat.format(memoryUsage.getInit() * 1.0 / 1024 / 1024) +
                "M");
        systemMemoryInfo.setJvmMaximumAvailableMemory(decimalFormat.format(memoryUsage.getMax() * 1.0 / 1024 / 1024) + "M");
        systemMemoryInfo.setJvmUsedMemory(decimalFormat.format(memoryUsage.getUsed() * 1.0 / 1024 / 1024) + "M");
        GlobalMemory memory = systemInfo.getHardware().getMemory();
        systemMemoryInfo.setPhysicalTotalMemory(decimalFormat.format(memory.getTotal() * 1.0 / 1024 / 1024 / 1024) +
                "G");
        systemMemoryInfo.setPhysicalRemainingMemory(decimalFormat.format(memory.getAvailable() * 1.0 / 1024 / 1024 / 1024) + "G");
        systemMemoryInfo.setPhysicalUsedMemory(decimalFormat.format((memory.getTotal() - memory.getAvailable()) * 1.0 / 1024 / 1024 / 1024) + "G");
        return systemMemoryInfo;
    }
    
    /**
     * 获取cpu信息
     *
     * @param systemInfo
     *         系统信息根对象(可为null)
     * @param decimalFormat
     *         数字格式化样式(可为null)
     *
     * @return cpu信息
     */
    public static SystemCpuInfo cpuInfo(SystemInfo systemInfo, DecimalFormat decimalFormat) {
        if (systemInfo == null) {
            systemInfo = new SystemInfo();
        }
        if (decimalFormat == null) {
            decimalFormat = new DecimalFormat("#.##");
        }
        CentralProcessor processor = systemInfo.getHardware().getProcessor();
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        try {
            // 睡眠1s
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long[] ticks = processor.getSystemCpuLoadTicks();
        long nice =
                ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        long irq =
                ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        long softirq =
                ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        long steal =
                ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
        long cSys =
                ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long user =
                ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long iowait =
                ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        long idle =
                ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
        
        SystemCpuInfo systemCpuInfo = new SystemCpuInfo();
        systemCpuInfo.setCoreNumber(processor.getLogicalProcessorCount());
        systemCpuInfo.setSystemUsageRate(decimalFormat.format(cSys * 1.0 / totalCpu));
        systemCpuInfo.setUserUsageRate(decimalFormat.format(user * 1.0 / totalCpu));
        systemCpuInfo.setCurrentIoWaitRate(decimalFormat.format(iowait * 1.0 / totalCpu));
        systemCpuInfo.setCurrentIdleRate(decimalFormat.format(idle * 1.0 / totalCpu));
        return systemCpuInfo;
    }
    
    /**
     * 获取磁盘信息
     *
     * @param decimalFormat
     *         数字格式化样式(可为null)
     *
     * @return 磁盘信息集合
     */
    public static List<SystemDiskInfo> diskInfo(DecimalFormat decimalFormat) {
        if (decimalFormat == null) {
            decimalFormat = new DecimalFormat("#.##");
        }
        File[] files = File.listRoots();
        List<SystemDiskInfo> diskInfoList = new ArrayList<>(8);
        for (File file : files) {
            String totalSpace = decimalFormat.format(file.getTotalSpace() * 1.0 / 1024 / 1024 / 1024) + "G";
            String freeSpace = decimalFormat.format(file.getFreeSpace() * 1.0 / 1024 / 1024 / 1024) + "G";
            String usableSpace = decimalFormat.format(file.getUsableSpace() * 1.0 / 1024 / 1024 / 1024) + "G";
            SystemDiskInfo systemDiskInfo = new SystemDiskInfo();
            systemDiskInfo.setPath(file.getPath());
            systemDiskInfo.setTotalSpace(totalSpace);
            systemDiskInfo.setFreeSpace(freeSpace);
            systemDiskInfo.setUsableSpace(usableSpace);
            diskInfoList.add(systemDiskInfo);
        }
        return diskInfoList;
    }
    
    /**
     * 获取其他信息信息
     *
     * @return 其他信息信息
     */
    public static SystemOtherInfo otherInfo() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SystemOtherInfo systemOtherInfo = new SystemOtherInfo();
        systemOtherInfo.setOsName(System.getProperty("os.name"));
        systemOtherInfo.setProgramStartTime(simpleDateFormat.format(new Date(ManagementFactory.getRuntimeMXBean().getStartTime())));
        String processName = ManagementFactory.getRuntimeMXBean().getName();
        systemOtherInfo.setProcessPid(processName.split("@")[0]);
        systemOtherInfo.setJavaHome(System.getProperty("java.home"));
        systemOtherInfo.setJavaVersion(System.getProperty("java.version"));
        systemOtherInfo.setUserHome(System.getProperty("user.home"));
        systemOtherInfo.setUserName(System.getProperty("user.name"));
        return systemOtherInfo;
    }
    
    /**
     * 测试
     */
    public static void main(String[] args) {
        System.err.println(systemRealTimeInfo(true));
        System.out.println(systemRealTimeInfo(false));
        System.out.println(JSON.toJSONString(systemRealTimeInfo()));
    }
    
}